package util

import (
	"Short_chain_cats/database"
	"context"
	"fmt"
	"strconv"
	"time"

	"github.com/dgrijalva/jwt-go"
)

const (
	SecretKey      = "short_chain_cats"
	AccessTokenExp = 1 * time.Minute
	//AccessTokenExp  = 48 * time.Hour
	RefreshTokenExp = 24 * time.Hour
)

type Claims struct {
	UserID   int    `json:"user_id"`
	UserRole string `json:"user_role"`
	jwt.StandardClaims
}

func GenerateToken(userID int, userRole string) (string, error) {
	accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, &Claims{
		UserID:   userID,
		UserRole: userRole,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(AccessTokenExp).Unix(),
		},
	})
	accessTokenString, err := accessToken.SignedString([]byte(SecretKey))
	if err != nil {
		return "", err
	}

	refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, &Claims{
		UserID: userID,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(RefreshTokenExp).Unix(),
		},
	})
	refreshTokenString, err := refreshToken.SignedString([]byte(SecretKey))
	if err != nil {
		return "", err
	}
	// Store Refresh Token in Redis
	err = database.RedisClient.Set(context.Background(), "refresh_token:"+strconv.Itoa(int(userID)), refreshTokenString, RefreshTokenExp).Err()
	if err != nil {
		return "", err
	}
	return accessTokenString, nil
}

func RefreshToken(userID int, userRole string) (string, error) {
	// 从Redis中获取refresh token
	refreshTokenString, err := database.RedisClient.Get(context.Background(), fmt.Sprintf("refresh_token:%d", userID)).Result()
	if err != nil {
		return "", err
	}
	// Validate Refresh Token
	token, err := jwt.ParseWithClaims(refreshTokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(SecretKey), nil
	})
	if claims, ok := token.Claims.(*Claims); ok && token.Valid {
		if claims.UserID != userID {
			return "", jwt.NewValidationError("invalid user id", jwt.ValidationErrorClaimsInvalid)
		}
		// Generate new Access Token
		accessTokenString, _ := GenerateToken(userID, userRole)
		return accessTokenString, nil
	}
	return "", err
}
